//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <drvpub.h>
#include "ccamera.h"

EXTERN_C int  I2C_write_byte(char regaddr,char val);
EXTERN_C int  I2C_read_byte(char regaddr,char *buf);

//camera initilization
unsigned char InitialSequencce[][2] = {
   /* addr |  value */
   //reg group A
   {0x03, 0x00},
   {0x12, 0x05},    //AmpBias
   {0x13, 0x03},    //PixelBias     // for QSVGA
   {0x21, 0x00},    //Reserved
   {0x3C, 0x50},    //Reserved      // for QSVGA
   {0x3F, 0xE0},    //Reserved
   {0x42, 0xFF},    //Reserved
   {0x45, 0x34},
   {0x55, 0xFE},    //ISPContr ol1
   {0x59, 0xD3},    //Reserved
   //{0x5E, 0x04},  //LensRGai n
   {0x61, 0x6f},    //EdgeGain
   {0x62, 0x04},    //EdgeThre shold
   {0xA7, 0x31},    //WWContro l
   {0xA9, 0x44},    //AELock
   {0xb0, 0x00},
   {0xb1, 0x00},
   {0xB3, 0x11},    //Reserved
   {0xB6, 0x26},    //MaxExp(H )
   {0xB7, 0x20},    //MaxExp(L )
   {0xBA, 0x04},    //AWBSpeed
   {0x88, 0x42},    //CT0
   {0x89, 0x9A},    //CT1
   {0x8A, 0x88},    //CT2
   {0x8B, 0x8E},    //CT3
   {0x8C, 0x3E},    //CT4
   {0x8D, 0x90},    //CT5
   {0x8E, 0x87},    //CT6
   {0x8F, 0x96},    //CT7
   {0x90, 0x3d},    //CT8
   {0x64, 0x00},    //RedGC0
   {0x65, 0x10},    //RedGC1
   {0x66, 0x20},    //RedGC2
   {0x67, 0x2B},    //RedGC3
   {0x68, 0x36},    //RedGC4
   {0x69, 0x49},    //RedGC5
   {0x6A, 0x5A},    //RedGC6
   {0x6B, 0x7F},    //RedGC7
   {0x6C, 0x9B},    //RedGC8
   {0x6D, 0xBA},    //RedGC9
   {0x6E, 0xD4},    //RedGC10
   {0x6F, 0xEA},    //RedGC11
   {0x70, 0x00},    //GreenGC0
   {0x71, 0x10},    //GreenGC1
   {0x72, 0x20},    //GreenGC2
   {0x73, 0x2B},    //GreenGC3
   {0x74, 0x36},    //GreenGC4
   {0x75, 0x49},    //GreenGC5
   {0x76, 0x5A},    //GreenGC6
   {0x77, 0x7F},    //GreenGC7
   {0x78, 0x9B},    //GreenGC8
   {0x79, 0xBA},    //GreenGC9
   {0x7A, 0xD4},    //GreenGC1 0
   {0x7B, 0xEA},    //GreenGC1 1
   {0x7C, 0x00},    //BlueGC0
   {0x7D, 0x10},    //BlueGC1
   {0x7E, 0x20},    //BlueGC2
   {0x7F, 0x2B},    //BlueGC3
   {0x80, 0x36},    //BlueGC4
   {0x81, 0x49},    //BlueGC5
   {0x82, 0x5A},    //BlueGC6
   {0x83, 0x7F},    //BlueGC7
   {0x84, 0x9B},    //BlueGC8
   {0x85, 0xBA},    //BlueGC9
   {0x86, 0xD4},    //BlueGC10
   {0x87, 0xEA},    //BlueGC11

   //reg group B
   {0x03, 0x01},
   {0x04, 0x10},    //Reserved
   {0x05, 0x10},    //Reserved
   {0x06, 0x10},    //Reserved
   {0x07, 0x10},    //Reserved
   {0x08, 0x13},    //Reserved
   {0x0A, 0x00},    //Reserved
   {0x0B, 0x10},    //Reserved
   {0x0C, 0x20},    //Reserved
   {0x0D, 0x18},    //Reserved
   {0x22, 0x01},    //Reserved
   {0x23, 0x60},    //Reserved
   {0x25, 0x08},    //Reserved
   {0x26, 0x82},    //Reserved
   {0x2E, 0x0F},    //Reserved
   {0x2F, 0x1E},    //Reserved
   {0x30, 0x2D},    //Reserved
   {0x31, 0x3C},    //Reserved
   {0x32, 0x4B},    //Reserved
   {0x33, 0x5A},    //Reserved
   {0x34, 0x69},    //Reserved
   {0x35, 0x78},    //Reserved
   {0x36, 0x87},    //Reserved
   {0x37, 0x96},    //Reserved
   {0x38, 0xA5},    //Reserved
   {0x39, 0xB4},    //Reserved
   {0x3A, 0xC3},    //Reserved
   {0x3B, 0xD2},    //Reserved
   {0x3C, 0xE1},    //Reserved
   {0x3D, 0xF0},    //Reserved
   {0x3E, 0xFF},    //Reserved
   {0x3F, 0xFF},    //Reserved
   {0x40, 0xFF},    //Reserved
   {0x41, 0xFF},    //Reserved
   {0x42, 0xFF},    //Reserved
   {0x43, 0xFF},    //Reserved

   //reg group A
   {0x03, 0x00},
   // 50hz flicker setting at 13Mhz in QSUB sampling mode //
   {0x4d, 0x40},
   {0x51, 0x1d},
   {0x52, 0xc0},
   {0x53, 0x18},
   {0x54, 0xcc},
//   {0x20, 0x94},  //QSVGA
   {0x20, 0x91},    // QSVGA SVGA
   {0x0e, 0x04},
   {0x0f, 0xc1},    //Window_Y2  increase FrameHeight
   {0x1E, 0x06},    //TgContro l2
   {0x25, 0x02},    //Reserved
   {0x95, 0x19},    //Brightness
   {0x96, 0x9A},    //Contrast
   {0xae, 0x6b},    //Y target
   //enviroment setting for capture and preview;
   //day mode, auto set indoor and outdoor
   {0xB4, 0x09},
   {0xB5, 0x88},
   {0xB6, 0x13},    // 2x gain
   {0xB7, 0x10},                              //#119
   //end env setting
};

EXTERN_C void SensorInit()
{
    int i;
    char ID[3];

    DBGOUT(kprintf("PO1200N SensorInit\n"));

    I2C_read_byte(0x0, &ID[0]);
    I2C_read_byte(0x1, &ID[1]);
    I2C_read_byte(0x2, &ID[2]);

    DBGOUT(kprintf("PO1200N Product ID:\n"));
    for( i = 0; i< 3;i++) {
        DBGOUT(kprintf("ID[%d] = 0x%x\n", i, ID[i]));
    }


    DBGOUT(kprintf("\nStart writing initial data through i2c\n"));
    for(i = 0; i < 119; i++) {
        I2C_write_byte(InitialSequencce[i][0], InitialSequencce[i][1]);
    }

    DBGOUT(kprintf("After writing initial data through i2c\n\n"));
}

EXTERN_C ECode SensorSetCaptureFormat(PCamDrv_Context pCameraCx)
{
    return NOERROR;
}

EXTERN_C ECode SensorStartCapture(PCamDrv_Context pCameraCxt)
{
    return NOERROR;
}

EXTERN_C ECode SensorStopCapture(PCamDrv_Context camera_context)
{
    return NOERROR;
}

EXTERN_C ECode SensorSetBrightness(int Brightness)
{
    int bright = Brightness;

    DBGOUT(kprintf("SensorSetBright %d\n", Brightness));
    // Brightness(80h) : (bit7)|(bit6~bit0) = sign digit | magnitude
    // [0, 49][50, 99] -> [0xFF(-128), 0x81(-1)][0x00(0), 0x7F(127)]
    if (Brightness >= 0 && Brightness <= 49) {
        bright = 127 - bright * 127 / 49;
        bright |= 0x80;
    }
    else if (Brightness> 49 && Brightness <= 99) {
    	bright = 127 * (Brightness - 50) / 49;
    }
    else {
	    return E_ACCESS_DENIED;
    }
    I2C_write_byte((char)0x95, (char)bright);

    return NOERROR;
}

EXTERN_C ECode SensorSetContrast(int Contrast)
{
    int contrast = Contrast;

    DBGOUT(kprintf("SensorSetContrast %d\n", Contrast));
    // [0, 99] -> [0, 255]
    if (contrast < 0 || contrast > 99) return E_ACCESS_DENIED;
    contrast = 255 * contrast / 99;
    I2C_write_byte((char)0x96, (char)contrast);

    return NOERROR;
}

// TO BE DO MORE...
EXTERN_C ECode SensorSetSharpness(int Sharpness)
{
    DBGOUT(kprintf("SensorSetSharpness %d\n", Sharpness));
    switch (Sharpness)
    {
        case 0:
            I2C_write_byte((char)0x61, (char)0x60);
            break;
        case 1:
            I2C_write_byte((char)0x61, (char)0x64);
            break;
        case 2:
            I2C_write_byte((char)0x61, (char)0x68);
            break;
        case 3:
            I2C_write_byte((char)0x61, (char)0x6C);
            break;
        case 4:
			I2C_write_byte((char)0x61, (char)0x70);
			break;
        case 5:
			I2C_write_byte((char)0x61, (char)0x74);
			break;
        case 6:
			I2C_write_byte((char)0x61, (char)0x78);
			break;
        default:
            return E_ACCESS_DENIED;
    }
    I2C_write_byte((char)0x62, (char)0x06);

    return NOERROR;
}

EXTERN_C ECode SensorSetColorMode(int ColorMode)
{
    DBGOUT(kprintf("SensorSetColorMode %d\n", ColorMode));
    switch(ColorMode)
    {
        case CAMERA_CM_COLOR:
            I2C_write_byte((char)0x57, (char)0x0a);

            I2C_write_byte((char)0x99, (char)0x00);
            I2C_write_byte((char)0x9a, (char)0x00);
            break;
        case CAMERA_CM_BLACKANDWHITE:
            I2C_write_byte((char)0x57, (char)0x8a);
            I2C_write_byte((char)0x99, (char)0x80);
            I2C_write_byte((char)0x9a, (char)0x80);
            break;
        case CAMERA_CM_SEPIA:
            I2C_write_byte((char)0x57, (char)0x8a);
            I2C_write_byte((char)0x99, (char)0x98);
            I2C_write_byte((char)0x9a, (char)0x1c);
            break;
        case CAMERA_CM_WORM:
            I2C_write_byte((char)0x57, (char)0x8a);
            I2C_write_byte((char)0x99, (char)0x90);
            I2C_write_byte((char)0x9a, (char)0x28);
            break;
        case CAMERA_CM_COOL:
            I2C_write_byte((char)0x57, (char)0x8a);
            I2C_write_byte((char)0x99, (char)0x20);
            I2C_write_byte((char)0x9a, (char)0x98);
            break;
        case CAMERA_CM_REVERSE:
        case CAMERA_CM_EXPOSAL:
            return E_NOT_IMPLEMENTED;
#if 0
        case CMR_EFFECT_BULISH:
            I2C_write_byte((char)0x57, (char)0x8a);
            I2C_write_byte((char)0x99, (char)0x98);
            I2C_write_byte((char)0x9a, (char)0x98);
            break;

        case CMR_EFFECT_AQUA:
            I2C_write_byte((char)0x57, (char)0x8a);
            I2C_write_byte((char)0x99, (char)0x28);
            I2C_write_byte((char)0x9a, (char)0xa0);
            break;
#endif
        default:
            return E_ACCESS_DENIED;
    }

    return NOERROR;
}

// Only support day & night mode now.
EXTERN_C ECode SensorSetSceneMode(int SceneMode)
{
    DBGOUT(kprintf("SensorSetSceneMode %d\n", SceneMode));
    switch(SceneMode)
    {
        case CAMERA_SM_AUTO:
        case CAMERA_SM_PORTRAIT:
        case CAMERA_SM_SPORT:
        case CAMERA_SM_LANDSCAPE:
        case CAMERA_SM_CLOSEUP:
            // auto set indoor and outdoor
            I2C_write_byte((char)0x03, (char)0x00);
            I2C_write_byte((char)0xB4, (char)0x09);
            I2C_write_byte((char)0xB5, (char)0x88);
            I2C_write_byte((char)0xB6, (char)0x13); // 2x gain
            I2C_write_byte((char)0xB7, (char)0x10);
            break;

        case CAMERA_SM_NIGHT:
            I2C_write_byte((char)0x03, (char)0x00);
            I2C_write_byte((char)0xB4, (char)0x1C);
            I2C_write_byte((char)0xB5, (char)0x98);
            I2C_write_byte((char)0xB6, (char)0x72);
            I2C_write_byte((char)0xB7, (char)0x60);
            break;

        default:
            return E_ACCESS_DENIED;
    }

    return NOERROR;
}

EXTERN_C ECode SensorAdjustImage()
{
    char status;

    I2C_read_byte((char)0x20, &status);
    DBGOUT(kprintf("status = 0x%x\n", status));

    // init mirror
    I2C_write_byte((char)0x03,(char)0x00);
    I2C_write_byte((char)0xa8,(char)0x80); //
    I2C_read_byte(0x1d, &status);
    status |= 0x80;
    I2C_write_byte(0x1d,status);
    I2C_read_byte(0x1e, &status);
    status |= 0xc0;     //temp = 11xx xxxx
    status &= 0xc0;     //temp = 1100 0000      //BJ Lee 2005. 5.23
    status = status + 6;    //temp = 1100 0110      //BJ Lee 2005. 5. 23
    I2C_write_byte(0x1e,status);

    DzDelay(100);
    while(1)  //waiting set to register 1d Drop to '1'
    {
        I2C_read_byte(0x1d, &status);
//        DBGOUT(kprintf("Register 1dh = 0x%02x\n", status));
        if(status & 0x80 )
        {
            break;
        }
        else
        {
            DBGOUT(kprintf("the DROP didn't set to 1\r\n"));
            DzDelay(600);
       }
    }

    I2C_read_byte(0x1d, &status);
    status &= 0x7f;
    I2C_write_byte(0x1d,status);
    I2C_write_byte((char)0xa8,(char)0x8f);
    return NOERROR;
}

EXTERN_C void SensorBeforeStillCapture()
{
    char value;

    I2C_read_byte(0x1D, &value);
    value |= 0x80;
    I2C_write_byte(0x1D, value);

    I2C_read_byte(0x1E, &value);
    value |= 0x10; // set SN
    I2C_write_byte(0x1E, value);

    I2C_write_byte(0x61, (char)0xAC);

    while (1)  //waiting set to register 1d Drop to '1'
    {
        I2C_read_byte(0x1D, &value);
//        DBGOUT(kprintf("register 1dh = 0x%02x\n", value));
        if(value & 0x80 )
        {
            break;
        }
        else
        {
           DBGOUT(kprintf("the DROP didn't set to 1\n"));
           DzDelay(600);
        }
    }

    // Wait 1 preview frame time
    DzDelay(35000);

    I2C_read_byte(0x1F, &value);
    value |= 0x80;
    I2C_write_byte(0x1F, value);

}

EXTERN_C void SensorAfterStillCapture()
{
    char value;

    I2C_read_byte(0x1D, &value);
    value &= 0x7F;
    I2C_write_byte(0x1D, value);

    while(1)  //waiting set to register 1d Drop to '0'
    {
        I2C_read_byte(0x1D, &value);
        DBGOUT(kprintf("register 1dh = 0x%02x\n", value));
        if(value & 0x80 )
        {
            DBGOUT(kprintf("the DROP didn't set to 0\n"));
            DzDelay(60);
        }
        else
        {
            break;
        }
    }

    I2C_write_byte(0x61, (char)0xA6);

    I2C_read_byte(0x1E, &value);
    value &= ~0x10; // clear SN
    I2C_write_byte(0x1E, value);

}

static void SensorChangePhotoResolution(int PhotoResolution)
{
    char value;
    I2C_read_byte((char)0x20, &value);
    value &= 0xF0;

    Drv_Assert (PhotoResolution == CAMERA_PR_LOW ||
        PhotoResolution == CAMERA_PR_MEDIUM ||
        PhotoResolution == CAMERA_PR_HIGH );

    switch (PhotoResolution) {
        case CAMERA_PR_LOW:
            value |= 0x03;
            break;
        case CAMERA_PR_MEDIUM:
            value |= 0x01;
            break;
        case CAMERA_PR_HIGH:
            value |= 0x04;
            break;
    }
    I2C_write_byte((char)0x20, value);
    DzDelay(200000);
    DBGOUT(kprintf("SensorChangePhotoResolution Register 20h : 0x%x\n", value));
}

static void SensorChangeVideoResolution(int VideoResolution)
{
    char value;
    I2C_read_byte((char)0x20, &value);
    value &= 0x0F;

    Drv_Assert (VideoResolution == CAMERA_VR_LOW ||
        VideoResolution == CAMERA_VR_MEDIUM);
    switch (VideoResolution) {
        case CAMERA_VR_LOW:
            value |= 0x90;
            break;
        case CAMERA_VR_MEDIUM:
            value |= 0xC0;
            break;
        // CAMERA_VR_HIGH isn't used because of preview speed
//        case CAMERA_VR_HIGH:
//            value |= 0xC0;
//            break;
    }
    I2C_write_byte((char)0x20, value);
    DzDelay(200000);
    DBGOUT(kprintf("SensorChangeVideoResolution Register 20h : 0x%x\n", value));
}

#if 0
static void SensorSetWindowForHighResolution()
{
    // 768 * 1024
    // WindowX1: 679 (2A7h)
    I2C_write_byte(0x08, (char)0x02);
    I2C_write_byte(0x09, (char)0xA7);
    // WindowY1: 103d (67h)
    I2C_write_byte(0x0A, (char)0x00);
    I2C_write_byte(0x0B, (char)0x67);
    // WindowX2: 1447d (5A7h)
    I2C_write_byte(0x0C, (char)0x05);
    I2C_write_byte(0x0D, (char)0xA7);
    // WindowY2: 1127d (467h)
    I2C_write_byte(0x0E, (char)0x04);
    I2C_write_byte(0x0F, (char)0x67);

    // VsyncStart
    I2C_write_byte((char)0x9F, (char)0x00);
    I2C_write_byte((char)0xA0, (char)0x67);
    // VsyncStop
    I2C_write_byte((char)0xA1, (char)0x04);
    I2C_write_byte((char)0xA2, (char)0x67);
    // VsyncColumn
    I2C_write_byte((char)0xA3, (char)0x02);
    I2C_write_byte((char)0xA4, (char)0xA7);
}
#endif

EXTERN_C ECode SensorChangeResolution(PCamDrv_Context pCameraCxt,
        int AppPhotoResolution, int zoom)
{
    int OrgDrvPhotoResLevel, OrgDrvVideoResLevel;

    OrgDrvPhotoResLevel = pCameraCxt->DrvPhotoResLevel;
    OrgDrvVideoResLevel = pCameraCxt->DrvVideoResLevel;
    switch (AppPhotoResolution) {
        case CAMERA_PR_LOW:
        // Low quality, allow 1, 2, 4
        if (zoom == 1) {
            pCameraCxt->DrvPhotoResLevel = CAMERA_PR_LOW;
            pCameraCxt->DrvVideoResLevel = CAMERA_VR_LOW;
        }
        else if (zoom == 2) {
            pCameraCxt->DrvPhotoResLevel = CAMERA_PR_MEDIUM;
            pCameraCxt->DrvVideoResLevel = CAMERA_VR_MEDIUM;
        }
        else if (zoom == 4) {
            pCameraCxt->DrvPhotoResLevel = CAMERA_PR_HIGH;
            pCameraCxt->DrvVideoResLevel = CAMERA_VR_MEDIUM;
        }
        else {
            goto ParamError;
        }
        break;

        case CAMERA_PR_MEDIUM:
        // Low quality, allow 1, 2
        if (zoom == 1) {
            pCameraCxt->DrvPhotoResLevel = CAMERA_PR_MEDIUM;
            pCameraCxt->DrvVideoResLevel = CAMERA_VR_LOW;
        }
        else if (zoom == 2) {
            pCameraCxt->DrvPhotoResLevel = CAMERA_PR_HIGH;
            pCameraCxt->DrvVideoResLevel = CAMERA_VR_MEDIUM;
        }
        else {
            goto ParamError;
        }
        break;

        case CAMERA_PR_HIGH:
        // Low quality, only allow 1
        if (zoom == 1) {
            pCameraCxt->DrvPhotoResLevel = CAMERA_PR_HIGH;
            pCameraCxt->DrvVideoResLevel = CAMERA_VR_LOW;
        }
        else {
            goto ParamError;
        }
        break;
    }

    if (OrgDrvPhotoResLevel != pCameraCxt->DrvPhotoResLevel) {
        SensorChangePhotoResolution(pCameraCxt->DrvPhotoResLevel);
    }

    if (OrgDrvVideoResLevel != pCameraCxt->DrvVideoResLevel) {
        SensorChangeVideoResolution(pCameraCxt->DrvVideoResLevel);
    }

//    if (AppPhotoResolution == CAMERA_PR_HIGH) {
//        // The window rect is changed.
//        SensorSetWindowForHighResolution();
//    }

    return NOERROR;

ParamError:
    DBGOUT(kprintf("zoom %dx is not supported!\n", zoom));
    return E_INVALID_ARGUMENT;

}

// VideoWidth & VideoHeight: Reserved for _CamDrv_Resolution_App
static const CamDrv_Resolution _CamDrv_Resolution_App[] =
{
    {224, 300, 224, 300}, // CAMERA_PR_LOW
    {224, 300, 448, 600}, // CAMERA_PR_MEDIUM
    {224, 300, 768, 1024}  // CAMERA_PR_HIGH
};

// VideoWidth & VideoHeight: Used for preview
// PhotoWidth & PhotoHeight: Be equal to the corresponding
//      value of _CamDrv_Resolution_App
static const CamDrv_Resolution _CamDrv_Resolution_Drv[] =
{
    {224, 300, 224, 300}, // CAMERA_PR_LOW
    {224, 300, 448, 600}, // CAMERA_PR_MEDIUM
    {192, 256, 768, 1024}  // CAMERA_PR_HIGH
};

// Special for Zoom 4x, for high preview speed!
// Only in CAMERA_PR_LOW mode.
static const CamDrv_Resolution _CamDrv_Resolution_Drv_Zoom4 =
    {96, 128, 224, 300};

// Get the resolution that applications use
EXTERN_C ECode SensorGetAppResolution(PCamDrv_Context pCameraCxt,
        int PhotoResolution)
{
    unsigned int index = (unsigned int)PhotoResolution;

    if (index >=
        sizeof(_CamDrv_Resolution_App) / sizeof(CamDrv_Resolution)) {
        DBGOUT(kprintf("Unsupported photo quality value.\n"));
        return E_ACCESS_DENIED;
    }

    memcpy(&pCameraCxt->AppResolution,
            &_CamDrv_Resolution_App[index],
            sizeof(CamDrv_Resolution));

    return NOERROR;
}

// Get the resolution which the driver uses
EXTERN_C ECode SensorGetDrvResolution(PCamDrv_Context pCameraCxt,
        int AppPhotoResolution, int zoom)
{
    if (AppPhotoResolution == CAMERA_PR_LOW && zoom == 4) {
        // Special for Zoom 4x, for high preview speed!
        // Only in CAMERA_PR_LOW mode.
        memcpy(&pCameraCxt->DrvResolution,
                &_CamDrv_Resolution_Drv_Zoom4,
                sizeof(CamDrv_Resolution));
    }
    else {
        unsigned int index = (unsigned int)AppPhotoResolution;

        if (index >=
            sizeof(_CamDrv_Resolution_Drv) / sizeof(CamDrv_Resolution)) {
            DBGOUT(kprintf("Unsupported photo quality value.\n"));
            return E_ACCESS_DENIED;
        }
        memcpy(&pCameraCxt->DrvResolution,
            &_CamDrv_Resolution_Drv[index],
            sizeof(CamDrv_Resolution));
    }

    return NOERROR;
}

// Digital zoom: Only support 2x, 4x in low resolution,
// 2x in medium resolution.
// Adjust the window to get digital zoom effect.
//
// NOTE: Because of hardware bug, use only a subset of the whole capture
//       window. Because the first line will be discarded (also hardware bug?),
//       so there are more lines should be included. see function
//       CISetCaptureFormat in camera_ci.c.
EXTERN_C ECode SensorSetDigitalZoom(int zoom)
{
    switch (zoom) {
        // 896 * 1204
        // Original size
       case 1:
            // WindowX1: 615 (267h)
            I2C_write_byte(0x08, (char)0x02);
            I2C_write_byte(0x09, (char)0x67);
            // WindowY1: 13d (0Dh)
            I2C_write_byte(0x0A, (char)0x00);
            I2C_write_byte(0x0B, (char)0x0D);
            // WindowX2: 1511d (5E7h)
            I2C_write_byte(0x0C, (char)0x05);
            I2C_write_byte(0x0D, (char)0xE7);
            // WindowY2: 1217d (4C1h)
            I2C_write_byte(0x0E, (char)0x04);
            I2C_write_byte(0x0F, (char)0xC1);

            // VsyncStart
            I2C_write_byte((char)0x9F, (char)0x00);
            I2C_write_byte((char)0xA0, (char)0x0D);
            // VsyncStop
            I2C_write_byte((char)0xA1, (char)0x04);
            I2C_write_byte((char)0xA2, (char)0xC1);
            // VsyncColumn
            I2C_write_byte((char)0xA3, (char)0x02);
            I2C_write_byte((char)0xA4, (char)0x67);
            break;
        // 448 * 604
        // 1/2 of original size, both width and height
        // NOTE: width of window should be 4 bytes aligned
        // Why the width is 832?
        // Because the width in all modes must be 16 bytes aligned,
        // including UXGA, SVGA, QSVGA mode.
        // The width in QSVGA set to be 208,
        // so the original window size is 208 * 4.
        // It's same in the following codes.
        case 2:
            // WindowX1: 839d (347h)
            I2C_write_byte(0x08, (char)0x03);
            I2C_write_byte(0x09, (char)0x47);
            // WindowY1: 314d (13Ah)
            I2C_write_byte(0x0A, (char)0x01);
            I2C_write_byte(0x0B, (char)0x3A);
            // WindowX2: 1287d (507h)
            I2C_write_byte(0x0C, (char)0x05);
            I2C_write_byte(0x0D, (char)0x07);
            // WindowY2: 918d (396h)
            I2C_write_byte(0x0E, (char)0x03);
            I2C_write_byte(0x0F, (char)0x96);

            // VsyncStart
            I2C_write_byte((char)0x9F, (char)0x01);
            I2C_write_byte((char)0xA0, (char)0x3A);
            // VsyncStop
            I2C_write_byte((char)0xA1, (char)0x03);
            I2C_write_byte((char)0xA2, (char)0x96);
            // VsyncColumn
            I2C_write_byte((char)0xA3, (char)0x03);
            I2C_write_byte((char)0xA4, (char)0x47);

            break;
        // 224 * 301
        // 1/4 of original size, both width and height
       // NOTE: width of window should be 4 bytes aligned
        case 4:
            // WindowX1: 951d (3B7h)
            I2C_write_byte(0x08, (char)0x03);
            I2C_write_byte(0x09, (char)0xB7);
            // WindowY1: 464d (1D0h)
            I2C_write_byte(0x0A, (char)0x01);
            I2C_write_byte(0x0B, (char)0xD0);
            // WindowX2: 1175d (4EFh)
            I2C_write_byte(0x0C, (char)0x04);
            I2C_write_byte(0x0D, (char)0x97);
            // WindowY2: 865d (361h)
            I2C_write_byte(0x0E, (char)0x03);
            I2C_write_byte(0x0F, (char)0x61);

            // VsyncStart
            I2C_write_byte((char)0x9F, (char)0x01);
            I2C_write_byte((char)0xA0, (char)0xD0);
            // VsyncStop
            I2C_write_byte((char)0xA1, (char)0x03);
            I2C_write_byte((char)0xA2, (char)0x61);
            // VsyncColumn
            I2C_write_byte((char)0xA3, (char)0x03);
            I2C_write_byte((char)0xA4, (char)0xB7);
            break;
        default:
            Drv_Assert(0);
    }

    DzDelay(100000);

    return NOERROR;
}
